package com.example.service.impl;

import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.common.Result;
import com.example.config.ClientConfig;
import com.example.model.bo.LoginInfo;
import com.example.model.bo.TracePathDto;
import com.example.service.IpfsService;
import com.example.service.LoginService;
import com.example.service.TracePathService;
import com.example.utils.ContractAddressManager;
import com.example.utils.LSHUtil;
import com.example.utils.PropertyUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.util.StringUtil;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.transaction.manager.AssembleTransactionProcessor;
import org.fisco.bcos.sdk.transaction.manager.TransactionProcessorFactory;
import org.fisco.bcos.sdk.transaction.model.dto.CallResponse;
import org.fisco.bcos.sdk.transaction.model.dto.TransactionResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

import cn.hutool.http.HttpRequest;

@Service
@Slf4j
public class TracePathServiceImpl implements TracePathService {
    private static List<String> uploadContent = new ArrayList<>();

    public static final String TraceToTheSourceABI = com.example.utils.IOUtil.readResourceAsString("abi/TraceToTheSource.abi");
    private final String TraceToTheSourceAddress = ContractAddressManager.getAddressFromContractNote("TraceToTheSource");

    public static final String UserRegistryABI = com.example.utils.IOUtil.readResourceAsString("abi/UserRegistry.abi");
    private String UserRegistryAddress = ContractAddressManager.getAddressFromContractNote("UserRegistry");


    @Autowired
    private Client client;

    AssembleTransactionProcessor txProcessor;
    String clientPri;
    @Autowired
    private IpfsService ipfsService;

    @Value("${check.user.url:http://10.124.16.21:18197}")
    private String checkUserUrl;

    @Autowired
    private LoginService loginService;


    @Override
    public List<String> tracePath(TracePathDto tracePathDto) throws Exception {
        // 加载登录信息
        String userName = PropertyUtil.getUserName();
        AccountManager.loadP12Account(client, ClientConfig.account_keyfile_path + userName, ClientConfig.account_password);
        this.txProcessor = TransactionProcessorFactory.createAssembleTransactionProcessor(this.client, this.client.getCryptoSuite().getCryptoKeyPair());
        this.clientPri = client.getCryptoSuite().getCryptoKeyPair().getHexPrivateKey();

        //获取传向智能合约的参数
        List<Object> args = new ArrayList<>();
        String senderUsername = tracePathDto.getSenderUsername();
        String receiverUsername = tracePathDto.getReceiverUsername();
        ArrayList<BigInteger> hashKey = tracePathDto.getLshHash();
        BigInteger[] hashKeyArr = hashKey.toArray(new BigInteger[0]);
        // log.info("哈希哈希哈希:{}", (Object) hashKeyArr);
        //添加参数
        Collections.addAll(args, hashKeyArr, ClientConfig.threshold, senderUsername, receiverUsername);
        //System.out.println(args);
        //log.info("参数：{}", args);
        //System.out.println("溯源的hashkey：" + Arrays.toString(hashKeyArr));
        TransactionResponse addRecord = this.txProcessor.sendTransactionAndGetResponse(TraceToTheSourceAddress, TraceToTheSourceABI, "findPath", args);

        ArrayList<String> str  = (ArrayList<String>) addRecord.getReturnObject().get(0);
        //log.info("返回值 : {}", str);

        return str;
    }



    @Override
    public Result DataTraceability(MultipartFile file, String receiverUserName) throws Exception {
        // 检查用户名对应的 .p12 文件是否已经存在
        File p12File = new File(ClientConfig.account_keyfile_path + receiverUserName + ".p12");
        if (!p12File.exists()) {
            // 如果文件存在，返回注册失败的消息
            Result result = checkUser(receiverUserName);
            if (!"0".equals(result.getCode())) {
                return Result.error("发送失败，用户不存在");
            }

            // 注册用户
            LoginInfo dto = new LoginInfo(receiverUserName, "12345678", true);
            Map<String, String> register = loginService.register(dto);
            log.info("自动注册结果:{}", register.toString());
        }

        try {
            //加载登录信息
            String userName = PropertyUtil.getUserName();
            String senderUserName = userName;
            if (senderUserName.endsWith(".p12")) {
                senderUserName = senderUserName.substring(0, senderUserName.length() - 4);
            }

            if (senderUserName.equals(receiverUserName)) {
                return Result.error("发送人和接收人不能相同！");
            }

            AccountManager.loadP12Account(client, ClientConfig.account_keyfile_path + userName, ClientConfig.account_password);
            this.txProcessor = TransactionProcessorFactory.createAssembleTransactionProcessor(this.client, this.client.getCryptoSuite().getCryptoKeyPair());
            this.clientPri = client.getCryptoSuite().getCryptoKeyPair().getHexPrivateKey();
            // 将文件上传到 IPFS，并返回 cid 加入到 dto 中
            String originalFilename = file.getOriginalFilename();


            File tempFile = File.createTempFile("upload_", "_" + originalFilename);
            file.transferTo(tempFile);
            String cid = ipfsService.uploadToIpfs(tempFile.getAbsolutePath());

            // 是否重复上传（发送人、接收人、发送内容不能完成相同）
            if (!isCheckRepeatUpload(senderUserName, receiverUserName, cid)) {
                return Result.error("发送人+接收人+文件内容不能重复发送！");
            }

            //封装智能合约函数所需的参数信息
            List<Object> args = new ArrayList<>();
            args.add(receiverUserName);
            //上传到区块链
            CallResponse getUserAddress = this.txProcessor.sendCall(this.client.getCryptoSuite().getCryptoKeyPair().getAddress(), this.UserRegistryAddress, UserRegistryABI, "getUserAddress", args);
            String receiverAddress = (String) getUserAddress.getReturnObject().get(0);

            //清空参数列表
            args.clear();
            //获取文件在局部敏感哈希下的哈希数组
            long[] hashKey = LSHUtil.getHashKey(tempFile.getAbsolutePath());
            BigInteger[] bigIntegers = new BigInteger[hashKey.length];
            //处理哈希数组，以便上传到去亏阿联
            for(int i = 0; i < hashKey.length; i++){
                bigIntegers[i] = BigInteger.valueOf(hashKey[i]);
            }
            // System.out.println("上传时的hashkey：" + Arrays.toString(bigIntegers));
            //获取上传时间
            LocalDateTime now = LocalDateTime.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            String formattedDateTime = now.format(formatter);
            //封装参数列表
            Collections.addAll(args, receiverAddress, senderUserName, receiverUserName, bigIntegers, cid, file.getOriginalFilename(), formattedDateTime, ClientConfig.threshold);
            //log.info("参数：{}", args);
            //上传至区块链形成交易区块
            TransactionResponse addRecord = this.txProcessor.sendTransactionAndGetResponse(TraceToTheSourceAddress, TraceToTheSourceABI, "fileCochain", args);
            //获取智能合约返回值
            ArrayList<String> str  = (ArrayList<String>) addRecord.getReturnObject().get(0);
            //反转返回值，形成上传路径
            Collections.reverse(str);
            log.info("返回值 : {}", str);
            //删除临时文件
            tempFile.delete();
            return Result.success(str);
        } catch (FileNotFoundException e) {
            return Result.error("文件被其他程序占用，无法访问。");
        } catch (IOException e) {
            return Result.error("文件上传失败，请检查是否文件被其他程序占用或拒绝访问(只读文件)");
        }
    }

    public static void main(String[] args) {
        String s = "sm1&sm2&QmXe9QJK22qHUQNDuebp8oEdJYbqwGuy1cWcsexKZ2Pajx";
        // 获取MD5实例
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("MD5");
            // 计算MD5值
            md.update(s.getBytes());
            byte[] digest = md.digest();
            // 将字节转换为十六进制字符串
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(String.format("%02x", b));
            }
            String res = sb.toString();
//            boolean checkRepeatUpload = isCheckRepeatUpload("sm1", "sm2", "QmXe9QJK22qHUQNDuebp8oEdJYbqwGuy1cWcsexKZ2Pajx");
            System.out.println(res);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

    }

    /**
     * @desc 是否重复上传（发送人、接收人、发送内容不能完成相同）
     * @author shishumin
     * @date 2024/12/16 15:53
     */
    public static  boolean isCheckRepeatUpload(String sedUser, String revUser, String cid) {
//        String cid = "QmXe9QJK22qHUQNDuebp8oEdJYbqwGuy1cWcsexKZ2Pajx";
//        String sedUser = "sm1";
//        String reUser = "sm2";
        String s = sedUser +"&"+ revUser +"&"+ cid;
//        a752ccc1a68098eeafc5bc9296e726c4
        try {
            // 获取MD5实例
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 计算MD5值
            md.update(s.getBytes());
            byte[] digest = md.digest();
            // 将字节转换为十六进制字符串
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(String.format("%02x", b));
            }
            String res = sb.toString();
            if (uploadContent.contains(res)) {
                return false;
            }
            // 输出MD5值
            uploadContent.add(res);
//            sm1&sm2&QmXe9QJK22qHUQNDuebp8oEdJYbqwGuy1cWcsexKZ2Pajx
//            sm1&sm1&QmXe9QJK22qHUQNDuebp8oEdJYbqwGuy1cWcsexKZ2Pajx
            log.info("MD5原始值:{},MD5结果值:{}", s, res);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return true;
    }

    public Result checkUser(String receiverUserName) {
        try {
            // 构建请求参数
            JSONObject param = new JSONObject();
            param.put("userName", receiverUserName);

            // 构建请求实体
            // 打印URL和参数
            log.info("请求URL: {}", checkUserUrl);
            log.info("请求参数: {}", param.toJSONString());
            HttpResponse response = HttpRequest.post(checkUserUrl)
                    .header("Is-Inner-Request", "true")
                    .body(JSON.toJSONString(param))
                    .execute();
            if(!response.isOk()) {
                log.debug("executeStatus:{}", response.isOk());
                return Result.error("发送失败，用户不存在");
            }

            // 打印返回值
            log.info("返回值: {}", response.body());
            JSONObject responseBody = JSONObject.parseObject(response.body(), JSONObject.class);
            if (responseBody.get("respCode") != null && !"200".equals(responseBody.getInteger("respCode").toString())) {
                return Result.error("发送失败，用户不存在");
            }

            if (responseBody.get("user") == null) {
                return Result.error("发送失败，用户不存在");
            }
            JSONObject user = responseBody.getObject("user", JSONObject.class);
            if (user == null) {
                return Result.error("发送失败，用户不存在");
            }
            String userId = user.getString("userId");
            if (StringUtil.isBlank(userId)) {
                return Result.error("发送失败，用户不存在");
            }
        } catch (RestClientException e) {
            e.printStackTrace();
            // 处理文件上传逻辑（省略）
            return Result.error("发送失败");
        }
        // 处理文件上传逻辑（省略）
        return Result.success("发送成功");
    }

}
